package be.ipl.finito.domaine;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MapKeyColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

import be.ipl.finito.util.Util;

@SuppressWarnings("serial")
@Entity
@Table(name="PARTIES" , schema="FINITO")

public class Partie implements Serializable{
	@Transient
	public static final int MAX_JOUEURS = 2;	
	@Transient
	public static final int TEMPS_DEBUT_PARTIE = 30;
	@Transient
	public static final int MIN_JOUEURS = 2;

	public enum Etat {
		EN_ATTENTE {
			@Override
			boolean annulerPartie(Partie partie) {
				return partie.getJoueurs().size() < 2 ;
			}
			
			boolean rejoindrePartie(Joueur joueur, Partie partie){
				Util.checkObject(joueur);
				Util.checkObject(partie);
				if(partie.getJoueurs().contains(joueur)){
					return false;
				}
				Grille grille = new Grille(joueur);
				partie.getGrilles().add(grille);
				if(partie.getGrilles().size() == MAX_JOUEURS){
					partie.commancerPartie();
				}
				return true;
			}
			
			boolean commancerPartie(Partie partie){
				Util.checkObject(partie);
				if(partie.getGrilles().size() < MIN_JOUEURS){
					return false;
				}	
				partie.etat = Etat.EN_COURS;
				return true;
			}
			
			boolean reprendrePartie(Partie partie, Joueur joueur){
				Util.checkObject(partie);
				Util.checkObject(joueur);
				if(!partie.getJoueurs().contains(joueur)){
					return false;
				}
				return true;
			}
		}, 
		EN_COURS {
			boolean abandoner(Partie partie, Joueur joueur){
				Util.checkObject(joueur);
				Util.checkObject(partie);
				if(!partie.getJoueurs().contains(joueur)){
					return false;
				}
				partie.etat = Etat.EN_ATTENTE;
				return true;
			}
			
			int lancerDe(Partie partie){
				Util.checkObject(partie);
				Random r = new Random();
					return (r.nextInt(20-1))+1;
			}
			
			Jeton tirage(Partie partie){
				Util.checkObject(partie);
				Jeton j = partie.getJeton().get(partie.indiceCourant++);
				for (Grille grille : partie.grilles) {
					if(!grille.ajouterJeton(j)){
						return null;
					}
				}
				return j;
			}
			
			boolean placerJeton(Partie partie, Case uneCase, Grille grille){
				Util.checkObject(partie);
				Util.checkObject(uneCase);
				Util.checkObject(grille);
				
				if(!grille.supprimerJeton(uneCase.getJeton())){
					return false;
				}
				return grille.placerJeton(uneCase);
			}
			
			boolean finirPartie(Partie partie){
				Util.checkObject(partie);
				partie.etat = Etat.FINIE;
				return true;
			}

		}, 
		FINIE {
			List<Grille> getClassement(Partie partie){
				calculerScore(partie);
				Collections.sort(partie.grilles, new Comparator<Grille>() {
					@Override
					public int compare(Grille g1, Grille g2) {
						return g2.getScore()-g1.getScore();
					}
				});
				return partie.grilles;
			}
			
			boolean calculerScore(Partie partie){
				for (Grille grille : partie.getGrilles()) {
					grille.calculerScore();
				}
				return false;
			}
		};
		
		boolean annulerPartie(Partie partie){
			return false;
		}
		 
		boolean rejoindrePartie(Joueur joueur, Partie partie){
			return false;
		}
		
		boolean commancerPartie(Partie partie){
			return false;
		}
		
		boolean abandoner(Partie partie , Joueur joueur){
			return false;
		}
			
		boolean reprendrePartie(Partie partie, Joueur joueur){
			return false;
		}
		
		List<Grille> getClassement(Partie partie){
			return null;
		}
		
		int lancerDe(Partie partie){
			return -1;
		}
		
		Jeton tirage(Partie partie){
			return null;
		}
		
		boolean placerJeton(Partie partie, Case uneCase, Grille grille){
			return false;
		}
		
		boolean calculerScore(Partie partie){
			return false;
		}
		
		boolean finirPartie(Partie partie){
			return false;
		}
		
	};
			
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Id
	private int idPartie;
	
	@NotNull
	@Min(1)
	@Max(13)
	private int indiceCourant;

	@ManyToMany
	@JoinTable(name="PARTIES_JETONS", schema="FINITO", joinColumns={@JoinColumn(name="PARTIE_ID")}, inverseJoinColumns={@JoinColumn(name="JETON_ID")})
	@MapKeyColumn(name="ORDRE")
	private Map<Integer,Jeton> jeton = new HashMap<Integer,Jeton>();

	@OneToMany(cascade=CascadeType.ALL)
	@JoinColumn(name="PARTIE_ID")
	private List<Grille> grilles = new ArrayList<Grille>();
	
	@NotNull
	@Enumerated
	private Etat etat = Etat.EN_ATTENTE;
		
	protected Partie(){
	}
	
	public Partie(Joueur joueur){
		rejoindrePartie(joueur);
		indiceCourant=1; 
	}

	public int getIdPartie() {
		return idPartie;
	}

	public int getIndiceCourant() {
		return indiceCourant;
	}

	public Map<Integer, Jeton> getJeton() {
		return jeton;
	}

	public List<Grille> getGrilles() {
		return grilles;
	}

	public Etat getEtat() {
		return etat;
	}
	
	public List<Joueur> getJoueurs(){
		List<Joueur> joueurs = new ArrayList<Joueur>();
		for (Grille grille : grilles) {
			joueurs.add(grille.getJoueur());
		}
		return joueurs;
	}
	
	public void setEtat(Etat etat) {
		this.etat = etat;
	}
	
	public boolean annulerPartie(){
		return this.etat.annulerPartie(this);
	}
	
	public boolean rejoindrePartie(Joueur joueur){
		return etat.rejoindrePartie(joueur, this);
	}

	public boolean finirPartie(){
		return etat.finirPartie(this);
	}
	
	public boolean calculerScore(){
		return etat.calculerScore(this);
	}
	
	public boolean reprendrePartie(Joueur joueur){
		return etat.reprendrePartie(this, joueur);
	}

	public boolean abandoner(Joueur joueur){
		return etat.abandoner(this , joueur);
	}
	
	public List<Grille> getClassement(){
		return etat.getClassement(this);
	}
	
	public int lancerDe(){
		return etat.lancerDe(this);
	}
	
	public Jeton tirage(){
		return etat.tirage(this);
	}
	
	public boolean placerJeton(Case uneCase, Grille grille){
		return etat.placerJeton(this, uneCase, grille);
	}
	
	public boolean commancerPartie(){
		return etat.commancerPartie(this);
	}
	
	public void setIdPartie(int idPartie) {
		this.idPartie = idPartie;
	}

	public void setIndiceCourant(int indiceCourant) {
		this.indiceCourant = indiceCourant;
	}

	public void setJeton(Map<Integer, Jeton> jeton) {
		this.jeton = jeton;
	}

	public void setGrilles(List<Grille> grilles) {
		this.grilles = grilles;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + idPartie;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Partie other = (Partie) obj;
		if (idPartie != other.idPartie)
			return false;
		return true;
	}
	
	
	
}
